#![cfg_attr(not(feature = "std"), no_std)]

use liquid::storage;
use liquid_lang as liquid;
use liquid_lang::State;

#[liquid::contract]
mod Ballot {
    use super::*;

    #[derive(Debug, State)]
    pub struct Proposal {
        id: u128,
        content: String,
        voteCount: u128
    }

    #[derive(Debug, State)]
    pub struct Voter {
        weight: u128,
        voted: bool,
        delegate: String,
        proposal: u32
    }
    
    #[liquid(storage)]
    struct Ballot {
        proposals: storage::Vec<Proposal>,
        voters: storage::Mapping<address, Voter>,
        host: storage::Value<address>
    }

    #[liquid(methods)]
    impl Ballot {
        pub fn new(&mut self) {
            self.proposals.initialize();
            self.proposals.push(Proposal{id: 1, content: String::from("proposal_1"), voteCount: 0});
            self.proposals.push(Proposal{id: 2, content: String::from("proposal_2"), voteCount: 0});
            self.proposals.push(Proposal{id: 3, content: String::from("proposal_3"), voteCount: 0});
            self.voters.initialize();
            self.host.initialize(self.env().get_caller());
        }

        pub fn SetVoter(&mut self, voterAddr: address) -> bool{
            if self.env().get_caller() != *self.host.get() {
                return false
            }
            let opt = self.voters.insert(&voterAddr, 
                Voter{weight: 1, voted: false, delegate: address::empty().to_string(), proposal: 0});
            match opt {
                None => return true,
                Some(Voter) => return false,
            }
        }
        
        pub fn Delegate(&mut self, deleTo: address) -> bool{
            let callAddr = self.env().get_caller();
            if callAddr == deleTo {
                return false
            }

            let caller = &mut self.voters[&callAddr];
            if caller.voted == true {
                return false
            }

            caller.voted = true;
            caller.delegate = deleTo.to_string();
            let callerWeight = caller.weight;
            
            let mut delAddr = deleTo;
            let mut nexDel = self.voters[&delAddr].delegate.clone();
            while nexDel.parse<address>() != address::empty() {
                if nexDel.parse<address>() == callAddr {
                    return false
                }
                delAddr = nexDel.parse<address>().clone();
                nexDel = self.voters[&nexDel.parse<address>()].delegate.clone();
            }
            let delVoter = &mut self.voters[&delAddr];
            if delVoter.voted == true {
                self.proposals[delVoter.proposal].voteCount += callerWeight;
            }
            else {
                delVoter.weight += 1;
            }
            true
        }
        

        pub fn Vote(&mut self, proposalId: u128) -> bool{
            let mut curVoter = self.voters.get_mut(&self.env().get_caller()).unwrap();
            if(curVoter.voted == true) {
                return false
            }
            for i in 0..self.proposals.len() {
                if self.proposals[i].id == proposalId {
                    self.proposals[i].voteCount += curVoter.weight;
                    curVoter.voted = true;
                    curVoter.proposal = i;
                }
            }
            true
        }

        fn GetWinnerIndex(&self) ->u32 {
            let mut index = 0;
            let mut max = 0;
            for i in 0..self.proposals.len() {
                if self.proposals[i].voteCount>max {
                    index = i;
                    max = self.proposals[i].voteCount;
                }
            }
            index
        }

        pub fn GetMaxCount(&self) -> u128 {
            self.proposals[self.GetWinnerIndex()].voteCount
        }

        pub fn GetWinnerContent(&self) ->String {
            self.proposals[self.GetWinnerIndex()].content.clone()
        }

    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
        use liquid::env::test;

        #[test] 
        fn SetVoter_Test(){
            //指定创建合约的账户为alice
            let accounts = test::default_accounts();
            test::set_caller(accounts.alice);
            let mut contract = Ballot::new();
            assert_eq!(contract.SetVoter(accounts.bob), true);
            //账户地址不能重复
            assert_eq!(contract.SetVoter(accounts.bob), false);

            contract.SetVoter(accounts.charlie);
            contract.SetVoter(accounts.david);
            contract.SetVoter(accounts.eve);
            //测试设置多个账户
            assert_eq!(contract.voters.len(), 4);

            //非创建合约的账户不能指定投票者
            test::set_caller(accounts.bob); 
            assert_eq!(contract.SetVoter(accounts.frank), false);
        }

        #[test] 
        fn Delegate_Test(){
            let accounts = test::default_accounts();
            test::set_caller(accounts.alice);
            let mut contract = Ballot::new();
            contract.SetVoter(accounts.bob);
            contract.SetVoter(accounts.charlie);
            contract.SetVoter(accounts.david);
            
            //bob委托charlie
            test::set_caller(accounts.bob);
            contract.Delegate(accounts.charlie);
            assert_eq!(contract.voters[&accounts.bob].delegate, accounts.charlie);
            //charlie的权重变为2
            assert_eq!(contract.voters[&accounts.charlie].weight, 2);
            //委托后不能投票
            assert_eq!(contract.Vote(1), false);

            //测试委托传递
            test::set_caller(accounts.david);
            contract.Delegate(accounts.bob);
            assert_eq!(contract.voters[&accounts.charlie].weight, 3);
            
            //禁止闭环委托
            test::set_caller(accounts.charlie);
            assert_eq!(contract.Delegate(accounts.david), false);
        }

        #[test] 
        fn Vote_Test(){
            let accounts = test::default_accounts();
            test::set_caller(accounts.alice);
            let mut contract = Ballot::new();
            contract.SetVoter(accounts.bob);
            contract.SetVoter(accounts.charlie);

            //测试投票，获得投票的提案票数增加
            test::set_caller(accounts.bob);
            contract.Vote(1);
            assert_eq!(contract.proposals[0].voteCount, 1);
            //不能多次投票
            assert_eq!(contract.Vote(2), false);

            test::set_caller(accounts.david);
            contract.Vote(2);
            assert_eq!(contract.proposals[1].voteCount, 2);
            
            //委托已经投票的账户，被委托的账户先前投票的提案得票数增加
            test::set_caller(accounts.eve);
            contract.Delegate(accounts.bob);
            assert_eq!(contract.proposals[0].voteCount, 2);

        }

        #[test] 


        #[test] 
        fn GetWinnerContent(){
            let accounts = test::default_accounts();
            test::set_caller(accounts.alice);
            let mut contract = Ballot::new();
            contract.SetVoter(accounts.bob);

            test::set_caller(accounts.bob);
            contract.Vote(1);
            assert_eq!(contract.GetWinnerContent(), "proposal_1");
        }
    }
}